import { DataSource } from "typeorm";
import { ResObjPropNameConst } from 'st-common-req'

/**
 * 创建一个用于装饰方法的和事务相关的装饰器
 *
 * @returns {MethodDecorator} 一个用于装饰方法的装饰器
 */
export function Transaction(): MethodDecorator {
  /**
   * 该装饰器用于装饰方法后，会使用新方法替换原方法，并在原方法执行前开启事务，在原方法执行后
   * 提交或回滚事务
   *
   * target: 装饰器装饰的方法所在类的原型对象
   * propertyKey: 装饰器装饰的方法的名字
   * descriptor: 装饰器装饰的方法属性的属性值描述符对象，即对装饰的方法的描述
   */
  return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    // 保存原方法
    const originalMethod = descriptor.value;
    // 使用新方法替换原方法（这里为了确保 this 能够指向装饰器装饰的方法所在的类的实例，使用
    // 普通函数，普通函数的 this 指向在函数执行时确定，而箭头函数的 this 指向在函数定义时
    // 确定）
    descriptor.value = async function(...args: any[]) {
      // 开启事务（在此之前需要确保在装饰的方法所在的类中注入了 DataSource）
      const dataSource: DataSource = this.dataSource;
      const queryRunner = dataSource.createQueryRunner();
      await queryRunner.startTransaction();
      try {
        // 执行原方法（此时新方法的 this 指向装饰器装饰的方法所在的类的实例）
        const res = await originalMethod.apply(this, [...args, queryRunner]);
        // 如果原方法执行成功，提交事务
        if (res[ResObjPropNameConst.STATUS]) await queryRunner.commitTransaction();
        // 如果原方法执行失败，回滚事务
        else await queryRunner.rollbackTransaction();
        // 返回原方法的执行结果
        return res;
      } catch (e) {
        // 如果原方法抛出异常，回滚事务
        await queryRunner.rollbackTransaction();
        // 抛出原方法的异常
        throw e;
      } finally {
        // 释放连接资源
        await queryRunner.release();
      }
    };
  };
}